package demo.collision;

import java.util.ArrayList;

import org.lwjgl.input.Keyboard;

import engine.camera.FlyoverCamera;
import engine.core.scene.Scene;
import engine.gameObject.GameObject;
import engine.gameObject.collider.BoundingSphere;
import engine.gameObject.collider.SphereCollider;
import engine.gui.Action;
import engine.gui.GUIStyle;
import engine.gui.elements.Checkbox;
import engine.gui.elements.Label;
import engine.gui.elements.Panel;
import engine.gui.elements.RadioButtonGroup;
import engine.gui.elements.Slider;
import engine.light.EnvironmentLight;
import engine.util.collada.ColladaLoader;
import engine.util.draw.Sphere;
import engine.util.gui.Profiler;
import engine.util.math.Quaternion;
import engine.util.texture.TextureLoader;

public class CollisionScene extends Scene {

	private static final String SPHERE_ID = "sphere_id";
	private static final String BOUNDING_SPHERE_ID = "bounding_sphere_id";
	
	private GameObject obelisk;
	private GameObject mandragora;
	private Sphere sphere;
	private Label collision;
	
	public CollisionScene(String name) {
		super(name);
	}

	@Override
	public void init() {
		setupCamera();
		
		setupGUI();
		
		setupGameObjects();
		
		setupSky();
		
		setupLight();
	}
	
	private void setupCamera() {
		setCurrentCamera(new FlyoverCamera());
		getCurrentCamera().setNearPane(40);
		getCurrentCamera().setFarPane(200);
		getCurrentCamera().setEyeX(0);
		getCurrentCamera().setEyeY(3);
		getCurrentCamera().setEyeZ(-10);
		((FlyoverCamera)getCurrentCamera()).setSpeed(0.01f);
	}
	
	private void setupGUI() {
		setGUIStyle(GUIStyle.getDefaultStyle());
		
		Panel panel = new Panel(0, 0, 250, 330);
		panel.setSolid(true);
		panel.setAlign(Panel.ALIGN_LEFT);
		panel.setValign(Panel.ALIGN_TOP);
		getWindowManager().addComponent(panel);
		
		final RadioButtonGroup radio = new RadioButtonGroup(10, 10, 20, 20);
		radio.addEntry("Nothing");
		radio.addEntry("First level");
		radio.addEntry("Second level");
		radio.addEntry("Third level");
		radio.addEntry("Forth level");
		radio.addEntry("Fifth level");
		radio.addEntry("Sixth level");
		radio.setSelectedIndex(0);
		radio.setAction(new Action() {
			@Override
			public void performAction() {
				ArrayList<GameObject> list = findAll(SPHERE_ID);
				for (GameObject gameObject : list) {
					gameObject.getParent().removeChild(gameObject);
				}
				
				if(radio.getSelectedIndex() >=  1) {
					enableSpheres(radio.getSelectedIndex());
				}
			}
		});
		panel.addComponent(radio);
		
		final Checkbox checkbox = new Checkbox("Render bounding sphere", 10, 180, 125, 20);
		checkbox.setAction(new Action() {
			@Override
			public void performAction() {
				if(checkbox.isChecked()) {
					enableBoundingSpheres();
				} else {
					disableBoundingSpheres();
				}
			}
		});
		panel.addComponent(checkbox);
		
		panel.addComponent(new Label("Rotate: ", 10, 210, 50, 20));
		
		final Slider slider1 = new Slider(100, 210, 125, 20);
		slider1.setMinValue(0);
		slider1.setMaxValue(2 * Math.PI);
		slider1.setAction(new Action() {
			@Override
			public void performAction() {
				obelisk.setRotation(Quaternion.convRotAxisToQuat((float)slider1.getCurrentValue(), 0, 1, 0));
				mandragora.setRotation(Quaternion.convRotAxisToQuat((float)slider1.getCurrentValue(), 0, 0, 1));
			}
		});
		panel.addComponent(slider1);
		
		panel.addComponent(new Label("Translate: ", 10, 240, 50, 20));
		
		final Slider slider2 = new Slider(100, 240, 125, 20);
		slider2.setMinValue(-3);
		slider2.setMaxValue(3);
		slider2.setCurrentValue(0);
		slider2.setAction(new Action() {
			@Override
			public void performAction() {
				obelisk.setPosition(-3 + (float)slider2.getCurrentValue(), 0, 0);
				mandragora.setPosition(3 - (float)slider2.getCurrentValue(), 0, 0);
			}
		});
		panel.addComponent(slider2);
		
		panel.addComponent(new Label("Scale: ", 10, 270, 50, 20));
		
		final Slider slider3 = new Slider(100, 270, 125, 20);
		slider3.setMinValue(0.2);
		slider3.setMaxValue(3);
		slider3.setCurrentValue(1);
		slider3.setAction(new Action() {
			@Override
			public void performAction() {
				obelisk.setScale((float)slider3.getCurrentValue(), 1, 1);
				mandragora.setScale(1, (float)slider3.getCurrentValue(), 1);
			}
		});
		panel.addComponent(slider3);
		
		collision = new Label("No collision", 10, 300, 100, 20);
		panel.addComponent(collision);
		
		Profiler profiler = new Profiler(0, 0);
		profiler.setAlign(Profiler.ALIGN_RIGHT);
		getWindowManager().addComponent(profiler);
	}
	
	private void disableBoundingSpheres() {
		ArrayList<GameObject> find = findAll(BOUNDING_SPHERE_ID);
		
		for(GameObject g: find) {
			g.getParent().removeChild(g);
		}
	}
	
	private void enableBoundingSpheres() {
		addSphere(obelisk, obelisk.getPosition(), new float[]{0, 0, 0}, new float[]{1, 1, 1}, obelisk.getSurroundingBoundRadius(), BOUNDING_SPHERE_ID);
		//addSphere(mandragora, mandragora.getPosition(), new float[]{0, 0, 0}, mandragora.getSurroundingBoundRadius(), BOUNDING_SPHERE_ID);
		addSphere(mandragora.getChildren().get(0), mandragora.getChildren().get(0).getPosition(), new float[]{0, 0, 0}, new float[]{1, 1, 1}, mandragora.getChildren().get(0).getSurroundingBoundRadius(), BOUNDING_SPHERE_ID);
		addSphere(mandragora.getChildren().get(1), mandragora.getChildren().get(1).getPosition(), new float[]{0, 0, 0}, new float[]{1, 1, 1}, mandragora.getChildren().get(1).getSurroundingBoundRadius(), BOUNDING_SPHERE_ID);
	}
	
	private void enableSpheres(int level) {
		Quaternion oldPos = obelisk.getRotation();
		obelisk.setRotation(new Quaternion(0, 0, 0, 1));
		
		SphereCollider collider = (SphereCollider)obelisk.getCollider();
		enableSpheres(obelisk, collider.getChildren(), level);
		obelisk.setRotation(oldPos);
		
		oldPos = mandragora.getChildren().get(0).getRotation();
		mandragora.getChildren().get(0).setRotation(new Quaternion(0, 0, 0, 1));
		GameObject g = mandragora.getChildren().get(0);
		collider = (SphereCollider)g.getCollider();
		enableSpheres(g, collider.getChildren(), level);
		mandragora.getChildren().get(0).setRotation(oldPos);
		
		oldPos = mandragora.getChildren().get(1).getRotation();
		mandragora.getChildren().get(1).setRotation(new Quaternion(0, 0, 0, 1));
		g = mandragora.getChildren().get(1);
		collider = (SphereCollider)g.getCollider();
		enableSpheres(g, collider.getChildren(), level);
		mandragora.getChildren().get(1).setRotation(oldPos);
	}
	
	private void enableSpheres(GameObject parent, ArrayList<BoundingSphere> boundingSpheres, int level) {
		if(level <= 1) {
			if(boundingSpheres != null) {
				float radius = Math.max(Math.max(parent.getScaleX(), parent.getScaleY()), parent.getScaleZ());
				for(BoundingSphere s: boundingSpheres) {
					addSphere(parent, s.getOrigin().getPosition(), parent.getPosition(), parent.getScale(), s.getOrigin().getRadius() * radius, SPHERE_ID);
				}
			}
		} else {
			if(boundingSpheres != null) {
				for(BoundingSphere s: boundingSpheres) {
					enableSpheres(parent, s.getChildren(), level - 1);
				}
			}
		}
	}
	
	private void addSphere(GameObject parent, float[] position, float[] translate, float[] scale, float radius, String id) {
		GameObject gameObject = sphere.copy();
		
		gameObject.setScale(radius, radius, radius);
		gameObject.setPosition(position[0] * scale[0], position[1] * scale[1], position[2] * scale[2]);
		gameObject.translate(translate);
		gameObject.setId(id);
		
		parent.addChild(gameObject);
	}
	
	private void setupGameObjects() {
		obelisk = ColladaLoader.importFile("model/demo/terrain/obelisk.dae", 6);
		obelisk.setScript(new ObeliskScript(collision));
		obelisk.setPosition(-3, 0, 0);
		obelisk.setRigidbody(true);
		addGameObject(obelisk);
		
		mandragora = ColladaLoader.importFile("model/demo/terrain/mandragora.dae", 6);
		mandragora.setPosition(3, 0, 0);
		mandragora.getChildren().get(0).setColliding(true);
		mandragora.getChildren().get(1).setColliding(true);
		addGameObject(mandragora);
		
		sphere = new Sphere(1, 10, 10);
		sphere.setId(SPHERE_ID);
	}
	
	private void setupSky() {
		Sphere skySphere = new Sphere(11, 10, 10);
		skySphere.setTexture(TextureLoader.loadTexture("img/demo/sky/skysphere1.png"));
		skySphere.setRotation(Quaternion.convRotAxisToQuat((float)Math.toRadians(180), 1, 0, 0));
		setSkyObject(skySphere);
	}
	
	private void setupLight() {
		EnvironmentLight light = new EnvironmentLight();
		light.setAmbient(0.5f, 0.5f, 0.5f);
		light.setDirection(-1, 1, -1);
		light.setDiffuse(0.5f, 0.5f, 0.5f);
		addLight(light);
	}
	
	@Override
	public void keyPressed(int key) {
		if(key == Keyboard.KEY_ESCAPE) {
			getOpenGLView().setRunning(false);
		}
	}
}
